package cn.adkii.springframework.context.event;

import cn.adkii.springframework.beans.BeansException;
import cn.adkii.springframework.beans.factory.BeanFactory;
import cn.adkii.springframework.beans.factory.BeanFactoryAware;
import cn.adkii.springframework.context.ApplicationListener;
import cn.adkii.springframework.utils.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

/**
 * @author malingdi
 * @description 对事件广播器的公共方法提取。
 * @date 2023/11/28 15:02
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.remove(listener);
    }

    /**
     * 摘取符合广播事件的监听处理器。
     */
    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
        LinkedList<ApplicationListener> allListeners = new LinkedList<>();
        for (ApplicationListener<ApplicationEvent> listener : applicationListeners){
            if (supportEvent(listener, event)) allListeners.add(listener);
        }
        return allListeners;
    }

    /**
     * 监听器是否对该事件感兴趣
     * 它用于判断一个ApplicationListener是否对特定的事件感兴趣。
     *
     * 方法的参数包括一个ApplicationListener<ApplicationEvent>类型的applicationListener，表示要进行判断的监听器对象，以及一个ApplicationEvent类型的event，表示要判断的事件对象。
     *
     * 方法的实现逻辑如下：
     *
     * 首先，通过applicationListener.getClass()获取监听器对象的类对象listenerClass。
     * 根据不同的实例化类型（CglibSubclassingInstantiationStrategy和SimpleInstantiationStrategy），判断是否需要获取目标类对象。如果是CGLIB代理类，则通过listenerClass.getSuperclass()获取目标类对象；否则，目标类对象就是listenerClass本身。
     * 通过targetClass.getGenericInterfaces()[0]获取目标类对象的第一个泛型接口genericInterface。
     * 通过(ParameterizedType)genericInterface将genericInterface转换为参数化类型（即带有泛型信息的类型）。
     * 通过((ParameterizedType)genericInterface).getActualTypeArguments()[0]获取参数化类型的第一个实际类型参数actualTypeArgument。
     * 通过actualTypeArgument.getTypeName()获取实际类型参数的类名className。
     * 通过Class.forName(className)根据类名获取实际类型参数的类对象eventClassName。如果类名对应的类不存在，则抛出ClassNotFoundException。
     * 最后，通过eventClassName.isAssignableFrom(event.getClass())判断eventClassName所表示的类或接口是否与event.getClass()所表示的类或接口相同，或者是否是其超类或超接口。如果是，则返回true，表示该监听器对该事件感兴趣；否则，返回false，表示该监听器不对该事件感兴趣。
     * 简而言之，supportEvent方法根据监听器的泛型接口信息，判断监听器是否对指定的事件感兴趣。它通过比较事件的类或接口与泛型接口的实际类型参数之间的关系，来确定是否匹配。
     */
    protected boolean supportEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        // 按照CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型，需要判断后获取目标class
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];

        Type actualTypeArgument = ((ParameterizedType)genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;
        try {
            eventClassName = Class.forName(className);
        }catch (ClassNotFoundException e){
            throw new BeansException("wrong event class name：" + className);
        }

        // 判定此eventClassName 对象所表示的类或接口与指定的event.getClass() 参数所表示的类或接口是否相同，或是否是其超类或超接口。
        // isAssignableFrom 是用来判断子类和父类的关系的，或者接口的实现类和接口的关系的，默认所有的类的终极父类都是Object。如果 A.isAssignableFrom(B) 结果是true, 证明 B可以转换成A，也就是A由B转换而来
        return eventClassName.isAssignableFrom(event.getClass());

    }
}
